/**
 * Copyright (C), 2001-2023, www.bosssof.com.cn
 * FileName: JwtTokenBuilder
 * Author: Administrator
 * Date: 2023-02-28 22:00:21
 * Description:
 * <p>
 * History:
 * <author> Administrator
 * <time> 2023-02-28 22:00:21
 * <version> 1.0.0
 * <desc> 版本描述
 */
package com.sd365.common.util;

import cn.hutool.core.net.URLEncoder;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @ClassName: JwtTokenBuilder
 * @Description: 封装了JWT 规范的 token的基本方法，采用Builder模式构建 JwtToken
 * <p> 主要包含方法： 1逐步构建生成token 2  验证签名</p>
 * @Author: Administrator
 * @Date: 2023-02-28 22:00
 **/
@Component
public class JwtTokenBuilder extends AbstractTokenBuilder{
    /**
     * 签名算法常量
     */
    public static final String ALG_SHA2="sha2";
    /**
     * 签名算法常量
     */
    public static final String ALG_MD5="md5";

    /**
     *  token头 为JSONString 格式的字符串
     */
    private String head;
    /**
     * 载荷 为JSONString 格式的字符串
     */
    private String payload;
    /**
     * 签名 为String
     */
    private String signature;

    @Override
    public AbstractTokenBuilder buildHead(JSONObject head)  {
        if(head==null || head.get("alg")==null ) {
            throw new  RuntimeException("构建token头参数不能为空");
        }
        if(head.get("typ")==null)
        { head.put("typ","JWT");}
        //是否需要加密存入　head
        this.head=head.toJSONString();
        return this;
    }

    @Override
    public AbstractTokenBuilder buildPayload(JSONObject payload)  {
        if(payload==null) {throw new RuntimeException("构建token payload不能为空");}
        // 加密存入 body
        this.payload=payload.toJSONString();
        return this;
    }

    @Override
    public AbstractTokenBuilder buildSignature(String secret) {
        // 按签名协议构建生成签名存入
        this.signature=generateSignature(this.head,this.payload,secret);
        return this;
    }


    @Override
    public AbstractTokenBuilder build() {
        return this;
    }

    @Override
    public boolean verifySignature(JSONObject head, JSONObject payload, String signature, String secret) {
        if(head==null || payload==null
                || StringUtil.isEmpty(signature) || StringUtil.isEmpty(signature)){
            throw new RuntimeException("签名验证方法参数缺失");
        }
        // 按签名协议构建生成签名存入
       return signature.equals(generateSignature(head.toJSONString(),payload.toJSONString(),secret));
    }

    @Override
    public boolean verifySignature(String signature, String secret) {
        return signature.equals(generateSignature(this.head,this.payload,secret));
    }

    @Override
    public JSONObject toJSONObject() {
        JSONObject jwtTokenJSON=new JSONObject();
        jwtTokenJSON.put("head",this.head);
        jwtTokenJSON.put("payload",this.payload);
        jwtTokenJSON.put("signature",this.signature);
        return jwtTokenJSON;
    }

    @Override
    public List<String> toJSONList() {
        List<String> jwtTokenLst=new ArrayList<>();
        jwtTokenLst.add(head);
        jwtTokenLst.add(payload);
        jwtTokenLst.add(signature);
        return jwtTokenLst;
    }

    @Override
    public String toString() {
        // head payload 为 JSONString 类型
        return head+"."+payload+"."+signature;
    }

    /**
     * 生成签名的方法
     * @param head  用于产生签名的token的 head  各位 JSONString
     * @param payload 用于产生签名的token的 payload  各位 JSONString
     * @param secret 用于产生签名的 秘钥
     * @return
     */
    public String getSignature(String head , String payload, String secret){
        String signature="";
        byte[] signContent=(URLEncoder.createDefault().encode(head,UTF_8).toString()
                +"."+URLEncoder.createDefault().encode(payload,UTF_8).toString()).getBytes(UTF_8);
        switch (  JSONObject.parseObject(head).get("alg").toString().toUpperCase()){
            case "SHA2":
                signature= EncryptSimpleUtil.sha2(signContent,secret);
                break;
            case "MD5":
                signature= EncryptSimpleUtil.md5(signContent,secret);
                break;
        }
        return signature;
    }

    @Override
    protected String generateSignature(String head , String payload, String secret) {
        String signature="";
        byte[] signContent=(URLEncoder.createDefault().encode(head,UTF_8).toString()
                +"."+URLEncoder.createDefault().encode(payload,UTF_8).toString()).getBytes(UTF_8);
        switch (  JSONObject.parseObject(head).get("alg").toString().toUpperCase()){
            case "SHA2":
                signature= EncryptSimpleUtil.sha2(signContent,secret);
                break;
            case "MD5":
                signature= EncryptSimpleUtil.md5(signContent,secret);
                break;
        }
        return signature;
    }

}
